package com.ydl.learning.flink.demo.janino;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.codehaus.janino.ClassBodyEvaluator;
import org.codehaus.janino.ExpressionEvaluator;
import org.codehaus.janino.ScriptEvaluator;

/**
 * Janino使用demo
 * 动态编译
 * 官方文档 http://janino-compiler.github.io/janino/#janino-as-an-expression-evaluator
 *
 * @author ydl
 * @since 2022/1/13
 */
public class JaninoDemo {
    private static final Map<String, String> map = new HashMap<>();

    static {
        //用map模拟存储的脚本，脚本可以是很复杂，这里只作简单测试
        map.put("0", "return new EacProxy(){    public String query(){      return \"xxx交通管理局子公司分部一\";  }};");
        map.put("1", "return new EacProxy(){    public String query(){      return \"sss交通管理局子公司分部二\";  }};");
        map.put("2", "return new EacProxy(){    public String query(){      return \"阿里杭州萧山一公司\";   }};");
        map.put("3", "return new EacProxy(){    public String query(){      return \"菜鸟物流下沙分仓库\";   }};");
    }

    /**
     * 使用模板拼装方法
     */
    public static String generateProxySource(String source) throws IOException {
        String template = copyToString(JaninoDemo.class.getResourceAsStream("/EacProxy.temp"), StandardCharsets.UTF_8);
        return template.replaceAll("#replaced#", source);
    }

    /**
     * class加载
     */
    public static void classBody() throws Exception {
        //class body
        ClassBodyEvaluator ce = new ClassBodyEvaluator();
        String key = String.valueOf(new Random().nextInt(4));
        String source = generateProxySource(map.get(key));
        ce.cook(source);
        Object obj = ce.getClazz().getMethod("createProxy").invoke(null);
        EacProxy ep = (EacProxy) obj;
        System.out.println(ep.query());
        ce = new ClassBodyEvaluator();
        source = copyToString(JaninoDemo.class.getResourceAsStream("/EacProxy2.temp"), StandardCharsets.UTF_8)
                .replace("#replaced#", "entity.getName().equals(\"ydl\")");
        ce.cook(source);
        Entity entity = new Entity();
        entity.setAge(9);
        entity.setName("ydl");
        obj = ce.getClazz().getMethod("isTrue", Entity.class).invoke(null, entity);
        System.out.println(obj);
        obj = ce.getClazz().getMethod("nameCompare", Entity.class, String.class).invoke(null, entity, "ydl");
        System.out.println(obj);
        obj = ce.getClazz().getMethod("ageCompare", Entity.class, int.class).invoke(null, entity, 10);
        System.out.println(obj);
    }

    /**
     * 表达式
     */

    public static void expression() throws Exception {
        ExpressionEvaluator ee = new ExpressionEvaluator();
        ee.cook("3 + 4");
        System.out.println(ee.evaluate());
        //一个ExpressionEvaluator对象只能cook一次，下面会报错
        //ee.cook("1+1");
        ee = new ExpressionEvaluator();
        ee.cook("2+2");
        System.out.println(ee.evaluate());

        //上面的方式有个缺点，表达式是写死的，不能传参数，下面是可以传参数的表达式
        ee = new ExpressionEvaluator();

        // 定义参数名a,b和参数类型int
        ee.setParameters(new String[]{"a", "b"}, new Class[]{int.class, int.class});

        // 定义表达式的返回类型
        ee.setExpressionType(int.class);

        // "cook" (scan, parse, compile and load) 表达式
        ee.cook("a + b");

        // 传参，让表达式计算
        int result = (Integer) ee.evaluate(new Object[]{19, 23});
        System.out.println(result);

        ee = new ExpressionEvaluator();
        ee.setParameters(new String[]{"a", "b"}, new Class[]{String.class, String.class});
        ee.setExpressionType(boolean.class);
        ee.cook("a == b");
        System.out.println(ee.evaluate("aa", "aa"));

        //自定义函数
        ee = new ExpressionEvaluator();
        //导入自定义的类
        ee.setDefaultImports("com.ydl.janino.SelfExpression");
        ee.setParameters(new String[]{"a", "b"}, new Class[]{String.class, String.class});
        ee.setExpressionType(String.class);
        //调用自定义类里的方法
        ee.cook("SelfExpression.add(a,b)");
        System.out.println(ee.evaluate("aaa", "bbb"));
    }

    public static void script() throws Exception {
        ScriptEvaluator se = new ScriptEvaluator();
        se.cook(
                ""
                        + "static void method1() {\n"
                        + "    System.out.println(1);\n"
                        + "}\n"
                        + "\n"
                        + "method1();\n"
                        + "method2();\n"
                        + "\n"
                        + "static void method2() {\n"
                        + "    System.out.println(\"2222\");\n"
                        + "}\n"
        );
        se.evaluate(new Object[0]);
    }

    private static String copyToString(InputStream in, Charset charset) throws IOException {
        if (in == null) {
            return "";
        }

        StringBuilder out = new StringBuilder(4096);
        InputStreamReader reader = new InputStreamReader(in, charset);
        char[] buffer = new char[4096];
        int charsRead;
        while ((charsRead = reader.read(buffer)) != -1) {
            out.append(buffer, 0, charsRead);
        }
        return out.toString();
    }

    public static void main(String[] args) throws Exception {
        classBody();
        //expression();
        //script();
    }
}
